<template>
  <view v-if="visible" class="transition" :style="[styles]" @click="$emit('click')" @transitionend="transitionend">
    <slot />
  </view>
</template>

<script lang="ts" setup>
import type { PropType } from 'vue';
import { ref, computed, watch } from 'vue';
import { useNextTick } from '@/hooks';

const nextTick = useNextTick();

export type Mode = ('fade' | 'slide-top' | 'slide-right' | 'slide-bottom' | 'slide-left' | 'zoom-in' | 'zoom-out' | 'none')[];

const props = defineProps({
  show: Boolean,
  mode: Array as PropType<Mode>,
  duration: {
    type: Number,
    default: 300,
  },
});

const emit = defineEmits(['click', 'change']);

const visible = ref(false);
const active = ref(false);
const transitionPrepare = ref(false);

const styles = computed(() => {
  const styles = { opacity: 1, transitionDuration: transitionPrepare.value ? 0 : props.duration + 'ms' };
  const transform: string[] = [];

  props.mode?.forEach(mode => {
    switch (mode) {
      case 'fade':
        styles.opacity = active.value && !transitionPrepare.value ? 1 : 0;
        break;
      case 'slide-top':
        transform.push(`translateY(${active.value ? 0 : '-100%'})`);
        break;
      case 'slide-right':
        transform.push(`translateX(${active.value ? 0 : '100%'})`);
        break;
      case 'slide-bottom':
        transform.push(`translateY(${active.value ? 0 : '100%'})`);
        break;
      case 'slide-left':
        transform.push(`translateX(${active.value ? 0 : '-100%'})`);
        break;
      case 'zoom-in':
        transform.push(`scale(${active.value ? 1 : 0.95})`);
        break;
      case 'zoom-out':
        transform.push(`scale(${active.value ? 1 : 1.05})`);
        break;
      case 'none':
        styles.transitionDuration = 0
        break;
    }
  });

  return { ...styles, transform: transform.join(' ') };
});

function transitionend() {
  visible.value = active.value;

  emit('change', visible.value);
}

async function open() {
  visible.value = true;
  transitionPrepare.value = true;
  await nextTick();
  transitionPrepare.value = false;
  active.value = true;
}

function close() {
  active.value = false;
}

watch(() => props.show, (show) => show ? open() : close(), { flush: 'post', immediate: true });

defineExpose({ open, close });
</script>

<style scoped>
.transition {
  transition-timing-function: ease;
  transition-delay: 0ms;
  transition-property: all;
  opacity: 0;
}
</style>